#include <GL\glut.h>
#include "engine.h"
#include "drawing.h"
#include "engine.cpp"
#include "drawing.cpp"

void	Draw ();
void	MouseMove (int MouseX, int MouseY);
void	MousePressed (int Button, int State, int MouseX, int MouseY);
void	MousePressedMove (int MouseX, int MouseY);
void	Keyboard (unsigned char Key, int x, int y);
void	SKeyboard (int Key, int x, int y);
void	Timer (int);
void	Initialize (int argc, char **argv);

int main (int argc, char **argv)
{
	glutInit (&argc, argv);
	Win.W = glutGet (GLUT_SCREEN_WIDTH);
	Win.H = glutGet (GLUT_SCREEN_HEIGHT);
	Initialize (argc, argv);
	glutMainLoop ();
	return 0;
}

void Draw ()
{
	static bool ShowSettings = false;
	glClear (GL_COLOR_BUFFER_BIT);
	glPushMatrix ();
	if (!ShowSettings)
	{
		if (!Pause && EditObject == NoEditObject)
		{
			for (int j = 0; j < CountingAcuracy * SimulationSpeed; j++)
			{
				for (int i = 0; i < ObjectAmount; i++)
					if (Object[i].setup) Object[i].CountSpeed ();
				for (int i = 0; i < ObjectAmount; i++)
					if (Object[i].setup) Object[i].place += Object[i].speed / CountingAcuracy;
				for (int i = 0; i < ObjectAmount; i++) if (Object[i].setup)
					for (int k = i + 1; k < ObjectAmount; k++)
						if (Object[i].place.Distance (Object[k].place) < getMinDistance (i, k)
							&& Object[k].setup) Collide (i, k);
			};
		};
		SandBox (SBS);
		DrawSandBox (SBS);
		if (EditObject != NoEditObject && FocusCurrentObject && Object[EditObject].setup) Tr = Object[EditObject].place;
		if ((Mouse.x / Zoom) * Zoom <= - Win.W / 2 + 5) Tr.x -= Win.W * NavigationSpeed / Zoom;
		if ((Mouse.y / Zoom) * Zoom <= - Win.H / 2 + 5) Tr.y -= Win.H * NavigationSpeed / Zoom;
		if ((Mouse.x / Zoom) * Zoom >= Win.W / 2 - 5) Tr.x += Win.W * NavigationSpeed / Zoom;
		if ((Mouse.y / Zoom) * Zoom >= Win.H / 2 - 5) Tr.y += Win.H * NavigationSpeed / Zoom;
		CheckWindowPosition();
		DrawMenu ();

		for (int i = 0; i < ObjectAmount; i++)
		{
			if (Pause && Object[EditObject].type == Comet)
				for (int j = 0; j < TailLenth; j++)
					Object[EditObject].tail[j] = Object[EditObject].place;
			if (Object[i].setup && Object[i].type == Comet)
			{
				if (!Pause && EditObject == NoEditObject) UpdateTail (i);
				DrawObjectTail (i);
			};
			if (Object[i].exist == true && Object[i].type != NotExist && Skin == Usual)
				DrawObject (i);
		};

		if (EditObject != NoEditObject)
		{
			DrawObjectDirection (EditObject);
			DrawObjectMark (EditObject);
			DrawToolbar ();
		};
		DrawMiniMap ();
		if (AlertDelay > 0)
		{
			AlertDelay -= 1000 / FPS;
			ShowAlert ();
		};
	};
	glPopMatrix ();
	glutSwapBuffers ();
}

void MouseMove (int MouseX, int MouseY)
{
	Mouse.x = MouseX - Win.W / 2;
	Mouse.y = Win.H / 2 - MouseY;
}

void MousePressed (int Button, int State, int MouseX, int MouseY)
{
	Mouse.x = MouseX - Win.W / 2;
	Mouse.y = Win.H / 2 - MouseY;
	CurrentPressedButton = Button;
	if (State == GLUT_DOWN && Button < 3) // |Button < 3| ====>> this is not wheel
		MouseAction (CatchButton ());
	else
	{
		SpeedSetup = false;
		WeightSetup = false;
		WinPosSetup = false;
	}

	CurrentObjectAmount = 0;
	for (int i = 0; i < ObjectAmount; i++)
		if (Object[i].exist) CurrentObjectAmount++;
	
	switch (Button)
	{
	case 3: // mouse wheel forward
		if (Zoom < MaxZoom)
		{
			Zoom *= 1.1;
			Tr += (Mouse / Zoom) / 11.0;
			if (EditObject != NoEditObject)
				Tr = Object[EditObject].place;
		}
		if (Zoom > MaxZoom) Zoom = MaxZoom;
		CheckWindowPosition();
	break;
	case 4: // mouse wheel backward
		if (Zoom > MinZoom)
		{
			Zoom /= 1.1;
			if (EditObject != NoEditObject)
				Tr = Object[EditObject].place;
		}
		if (Zoom < MinZoom) Zoom = MinZoom;
		CheckWindowPosition();
	break;
	}
}

void MousePressedMove (int MouseX, int MouseY)
{
	// Mouse.x = (MouseX - Win.W / 2) / Zoom + Tr.x;
	// Mouse.y = (Win.H / 2 - MouseY) / Zoom + Tr.y;
	Mouse.x = MouseX - Win.W / 2;
	Mouse.y = Win.H / 2 - MouseY;
	Polar NewObjectSpeed;
	if (CurrentPressedButton == GLUT_LEFT_BUTTON && EditObject != NoEditObject && SpeedSetup)
	{
		NewObjectSpeed.angle = (Mouse / Zoom + Tr - Object[EditObject].place).Angle ();
		NewObjectSpeed.radius = Object[EditObject].place.Distance (Mouse / Zoom + Tr) / 10;
		if (NewObjectSpeed.radius > MaxObjectSpeed / 5)
			NewObjectSpeed.radius = MaxObjectSpeed / 5;
		Object[EditObject].speed = Decart (NewObjectSpeed);
	};
	if (CurrentPressedButton == GLUT_LEFT_BUTTON && WeightSetup)
		{
			Object[EditObject].weight = Mouse.x - 100;
			if (Object[EditObject].weight > MaxObjectWeight)
				Object[EditObject].weight = MaxObjectWeight;
			if (Object[EditObject].weight < MinObjectWeight)
				Object[EditObject].weight = MinObjectWeight;
		}

	if (CurrentPressedButton == GLUT_LEFT_BUTTON && WinPosSetup)
	{
		Tr.x = (Mouse.x * 20 - 9 * Win.W) * 20;
		Tr.y = (Mouse.y * 20 + 9 * Win.H) * 20;
		CheckWindowPosition();
	}
}

void Keyboard (unsigned char Key, int MouseX, int MouseY)
{
	if (!ShowSettings)
	switch (Key)
	{
		case 27: // Escape
			ShowSettings = true;
			exit(2);
		break;
		case 13: // Enter
			if (EditObject != NoEditObject)
				MouseAction (ToolbarAccept);
		break;
		case '+':
		case '=':
			if (CountingAcuracy < 100)
				CountingAcuracy++;
		break;
		case '-':
			if (CountingAcuracy > 3)
				CountingAcuracy--;
		break;
		case ' ':
			if (Pause) Pause = false; else Pause = true;
		break;
		case 'w':
			Tr.y += Win.H * NavigationSpeed / Zoom;
		break;
		case 's':
			Tr.y -= Win.H * NavigationSpeed / Zoom;
		break;
		case 'a':
			Tr.x -= Win.W * NavigationSpeed / Zoom;
		break;
		case 'd':
			Tr.x += Win.W * NavigationSpeed / Zoom;
		break;
		case 'c':
			ClearScreen ();
		break;
		case 72:
			FPS++;
		break;
		case 80:
			FPS--;
		break;
		default:
		break;
	}
}

void SKeyboard (int Key, int MouseX, int MouseY)
{
	if (glutGetModifiers() == 1)
	{
		switch(Key)
		{
			case GLUT_KEY_UP:

			break;
			case GLUT_KEY_DOWN:
			break;
			case GLUT_KEY_LEFT:
			break;
			case GLUT_KEY_RIGHT:
			break;
		}
	}
	else
	{
		switch(Key)
		{
		case GLUT_KEY_UP:
			Tr.y += Win.H * NavigationSpeed / Zoom;
		break;
		case GLUT_KEY_DOWN:
			Tr.y -= Win.H * NavigationSpeed / Zoom;
		break;
		case GLUT_KEY_LEFT:
			Tr.x -= Win.W * NavigationSpeed / Zoom;
		break;
		case GLUT_KEY_RIGHT:
			Tr.x += Win.W * NavigationSpeed / Zoom;
		break;
		case GLUT_KEY_PAGE_UP:
			if (Zoom < MaxZoom)
			{
				Zoom *= 1.1;
				Tr += (Mouse - Tr) / 11.0;
				if (EditObject != NoEditObject)
					Tr = Object[EditObject].place;
			}
			if (Zoom > MaxZoom) Zoom = MaxZoom;
			CheckWindowPosition();
		break;
		case GLUT_KEY_PAGE_DOWN:
			if (Zoom > MinZoom)
			{
				Zoom /= 1.1;
				if (EditObject != NoEditObject)
					Tr = Object[EditObject].place;
			}
			if (Zoom < MinZoom) Zoom = MinZoom;
			CheckWindowPosition();
		break;
		case GLUT_KEY_F4:
			if (glutGetModifiers() == 4) exit(0); // 4 - Alt, 2 - Ctrl, 1 - Shift
		break;
		}
	}
}

void Timer (int)
{
	glutPostRedisplay ();
	glutTimerFunc (1000 / FPS, Timer, 0);
}

void Initialize (int argc, char **argv)
{
	glutInitDisplayMode (GLUT_DOUBLE|GLUT_RGB);
	glutInitWindowSize (Win.W, Win.H);
	glutInitWindowPosition (60, 0);
	glutCreateWindow ("Gravity simulator version 3.1.7");
	glutFullScreen ();
	glutDisplayFunc (Draw);
	glutMotionFunc (MousePressedMove);
	glutPassiveMotionFunc (MouseMove);
	glutMouseFunc (MousePressed);
	glutKeyboardFunc (Keyboard);
	glutSpecialFunc (SKeyboard);
	glutTimerFunc (100, Timer, 0);
	glClearColor (0.0, 0.0, 0.0, 0.0);
	glLoadIdentity ();
	glOrtho (- Win.W / 2, Win.W / 2, - Win.H / 2, Win.H / 2, - 10 , 10);
	glMatrixMode (GL_MODELVIEW);
}

// TODO		Add checking Zoom position when changing zoom					{COMPLETED}
// TODO		Make drawing for contur and usual modes
// TODO		Make Metu and it's realization
// TODO		Rewrite key actions for toolbar
// TODO